package org.acooke.uykfd.walk

import java.sql.Connection

import org.acooke.uykfd.db.Schema
import org.acooke.uykfd.db.ArtistRow
import org.acooke.uykfd.db.TrackRow

import scala.collection.mutable.HashMap
import scala.collection.mutable.Queue
import scala.util.Random


class Candidates[T] {

  private val tiny = 1e-20
  private val huge = 1e20
  private val random = new Random
  private val scores = new HashMap[T, Double]

  def add(candidate: T, score: Double) = scores(candidate) = score

  def scale(candidate: T, factor: Double) =
    if (scores.contains(candidate)) {
      val score = scores(candidate) * factor
      scores(candidate) = Math.min(huge, Math.max(tiny, score))
    }

  def foreach = scores.keySet.foreach _

  def choose: T = {
//    for (candidate <- scores.keySet)
//      println(" " + scores(candidate) + " " + candidate)
    val total = scores.valuesIterator.reduceLeft(_+_)
    val pick = total * random.nextDouble
    var sweep = 0.0
    for (candidate <- scores.keySet) {
      sweep += scores(candidate)
//      println(total + " " + pick + " " + sweep + " " + scores(candidate) + " " + candidate)
      if (sweep >= pick) return candidate
    }
    throw new RuntimeException("Bad logic.")
  }

}


class History[T](size: Int) {

  private val queue = new Queue[T]
 
  def add(elem: T) {
    queue.enqueue(elem)
    if (queue.length > size) remove(queue.front)
  }

  protected def remove(elem: T) {queue.dequeue}

  def avoid(candidates: Candidates[T], factor: Double) =
    for (known <- queue) candidates.scale(known, factor)

  def oldest = queue.front

  def length = queue.length

}


class TrackHistoryForArtists(size: Int, cnxn: Connection) extends History[TrackRow](size) {

  private val counts = new HashMap[Int, Int]

  override def add(track: TrackRow) {
    check(track.artistId)
    counts(track.artistId) -= 1
    super.add(track)
  }

  private def check(artistId: Int) {
    if (! counts.contains(artistId))
      counts(artistId) = Schema.TRACKS.orm.countForArtistId(artistId, cnxn)
  }

  protected override def remove(track: TrackRow) {
    counts(track.artistId) += 1
    super.remove(track)
  }

  def weightArtists(candidates: Candidates[ArtistRow]) = 
    for (artist <- candidates) {
      check(artist.id)
      candidates.scale(artist, 0.1 + counts(artist.id))
    }

}
